﻿using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.Windows;
using SlimDX.DirectInput;
using Device = SlimDX.Direct3D11.Device;
using Resource = SlimDX.Direct3D11.Resource;
using System.Drawing;


namespace InsTron
{
    enum EProgramState
    {
        Menu,
        Lobby,
        Game
    }

    static class Program
    {        
        private static RenderForm form;
        private static Device device;
        private static DirectInput directInput;
        private static Keyboard keyboard;
        private static SwapChain swapChain;
        private static DeviceContext deviceContext;
        private static RenderTargetView renderTarget;
        private static IRenderable scene;
        private static EProgramState gameState;


        public static void ChangeGameState(EProgramState newState)
        {
            gameState = newState;
            if (scene != null)
            {
                scene.DisposeAll();
            }
            switch (gameState)
            {
                case EProgramState.Menu:
                    scene = new MenuView(device, form);
                    break;
                case EProgramState.Lobby:
                    scene = new LobbyView(device, form);
                    break;
                case EProgramState.Game:
                    scene = new GameView(device, keyboard);
                    break;
            }
        }


        private static void InitializeMainForm()
        {
            form = new RenderForm("InsTron");
            form.Size = new Size(Settings.CurrentResolution.Width, Settings.CurrentResolution.Height);
            form.FormBorderStyle = FormBorderStyle.FixedSingle;
            form.MaximizeBox = false;
            InitializeDebugKeys();
        }


        private static void InitializeDebugKeys()
        {
            form.KeyDown += (o, e) =>
            {
                switch (e.KeyCode)
                {
                    case Keys.M:
                        ChangeGameState(EProgramState.Menu);
                        break;
                    case Keys.L:
                        ChangeGameState(EProgramState.Lobby);
                        break;
                    case Keys.G:
                        ChangeGameState(EProgramState.Game);
                        break;
                    case Keys.NumPad8:
                        if (gameState == EProgramState.Game)
                        {
                            ((GameView)scene).MoveCameraTarget(0, 1, 0);
                        }
                        break;
                    case Keys.NumPad2:
                        if (gameState == EProgramState.Game)
                        {
                            ((GameView)scene).MoveCameraTarget(0, -1, 0);
                        }
                        break;
                    case Keys.D1:
                        Settings.CommitSettingsChange(false, "1280x720", false, Color.BlanchedAlmond, Color.BurlyWood, false);
                        break;
                    case Keys.D2:
                        Settings.CommitSettingsChange(false, "1280x720", false, Color.DarkGray, Color.GreenYellow, false);
                        break;
                    case Keys.D3:
                        Settings.CommitSettingsChange(true, "1280x720", false, Color.Green, Color.Yellow, false);
                        break;
                }
            };
        }


        private static void InitializeDevice()
        {
            var swapChainDescription = new SwapChainDescription
            {
                BufferCount = 1,
                Usage = Usage.RenderTargetOutput,
                OutputHandle = form.Handle,
                IsWindowed = true,
                ModeDescription = new ModeDescription(Settings.CurrentResolution.Width, Settings.CurrentResolution.Height, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                SampleDescription = new SampleDescription(1, 0),
                Flags = SwapChainFlags.AllowModeSwitch,
                SwapEffect = SwapEffect.Discard
            };

            Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.None, swapChainDescription, out device, out swapChain);

            deviceContext = device.ImmediateContext;
            device.Factory.SetWindowAssociation(form.Handle, WindowAssociationFlags.IgnoreAll);
            swapChain.IsFullScreen = Settings.Fullscreen;
        }


        public static void InitializeKeyboard()
        {
            directInput = new DirectInput();

            keyboard = new Keyboard(directInput);
            keyboard.SetCooperativeLevel(form, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
            keyboard.Acquire();
        }


        private static void InitializeTarget()
        {
            using (var resource = Resource.FromSwapChain<Texture2D>(swapChain, 0))
                renderTarget = new RenderTargetView(device, resource);
            deviceContext.OutputMerger.SetTargets(renderTarget);

            deviceContext.Rasterizer.SetViewports(new Viewport(0.0f, 0.0f, Settings.CurrentResolution.Width, Settings.CurrentResolution.Height));
        }


        public static void UpdateRenderSettings()
        {
            form.Width = Settings.CurrentResolution.Width;
            form.Height = Settings.CurrentResolution.Height;
            renderTarget.Dispose();
            swapChain.IsFullScreen = Settings.Fullscreen;
            swapChain.ResizeBuffers(1, Settings.CurrentResolution.Width, Settings.CurrentResolution.Height, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);
            InitializeTarget();
        }


        public static void RenderFrame()
        {
            deviceContext.ClearRenderTargetView(renderTarget, Color.Black);

            scene.RenderScene();
            swapChain.Present(0, PresentFlags.None);
        }


        private static void Cleanup()
        {
            scene.DisposeAll();
            renderTarget.Dispose();
            device.Dispose();
            swapChain.IsFullScreen = false;
            swapChain.Dispose();
            keyboard.Dispose();
            directInput.Dispose();
        }


        static void Main()
        {
            Settings.Initialize();
            Settings.DebugProfile();

            InitializeMainForm();
            InitializeDevice();
            InitializeKeyboard();
            InitializeTarget();

            ChangeGameState(EProgramState.Menu);

            MessagePump.Run(form, RenderFrame);

            Cleanup();
        }
    }
}

